<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>文件上传下载服务</title>
    <style>
      /* 页面主体样式 */
      body {
        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        max-width: 800px;
        margin: 0 auto;
        padding: 20px;
        line-height: 1.6;
      }
      /* 标题样式 */
      h1 {
        color: #333;
        text-align: center;
        margin-bottom: 30px;
      }
      /* 卡片容器样式 */
      .card {
        background-color: #f8f9fa;
        border-radius: 8px;
        padding: 20px;
        margin-bottom: 20px;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
      }
      /* 文件表单样式 */
      .file-form {
        display: flex;
        flex-direction: column;
        gap: 10px;
      }
      /* 按钮基本样式 */
      .btn {
        background-color: #4caf50;
        color: white;
        padding: 10px 15px;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 16px;
        transition: background-color 0.3s;
      }
      /* 按钮悬停效果 */
      .btn:hover {
        background-color: #45a049;
      }
      /* 次要按钮样式 */
      .btn-secondary {
        background-color: #6c757d;
      }
      /* 次要按钮悬停效果 */
      .btn-secondary:hover {
        background-color: #5a6268;
      }
      /* 进度条样式 */
      progress {
        width: 100%;
        height: 20px;
        margin-top: 10px;
      }
      /* 文件列表样式 */
      .file-list {
        list-style: none;
        padding: 0;
      }
      /* 文件项样式 */
      .file-item {
        display: flex;
        flex-direction: column;
        padding: 10px;
        border-bottom: 1px solid #ddd;
      }
      /* 文件项下载状态区域 */
      .download-status {
        margin-top: 5px;
        font-size: 0.9em;
        color: #666;
      }
      .file-item-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        width: 100%;
      }
      /* 最后一个文件项无底部边框 */
      .file-item:last-child {
        border-bottom: none;
      }
      /* 文件信息容器 */
      .file-info {
        display: flex;
        flex-direction: column;
      }
      /* 文件名样式 */
      .file-name {
        font-weight: bold;
      }
      /* 文件大小样式 */
      .file-size {
        color: #6c757d;
        font-size: 0.9em;
      }
      /* 文件操作按钮容器 */
      .file-actions {
        display: flex;
        gap: 10px;
      }
      /* 提示信息样式 */
      .info-text {
        font-size: 0.9em;
        color: #666;
        margin-top: 5px;
      }
      /* 分片下载进度条容器 */
      .chunk-progress-container {
        margin-top: 15px;
        padding: 10px;
        background-color: #f8f9fa;
        border-radius: 4px;
        display: block;
      }
      /* 分片进度条容器 */
      .chunk-progress {
        width: 100%;
        height: 20px;
        margin-bottom: 10px;
        position: relative;
        background-color: #fff;
        border-radius: 4px;
        overflow: hidden;
      }
      /* 进度条 */
      .chunk-progress progress {
        width: 100%;
        height: 100%;
        margin: 0;
      }
      /* 分片进度显示 */
      .chunk-label {
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        color: #333;
        font-size: 12px;
        line-height: 1;
        z-index: 1;
        text-shadow: 0 0 2px #fff;
      }
      /* 下载速度显示 */
      .download-speed {
        font-size: 0.8em;
        color: #666;
        margin-top: 5px;
      }
      /* 暂停/继续按钮 */
      .pause-resume-btn {
        background-color: #ffc107;
      }
      .pause-resume-btn:hover {
        background-color: #e0a800;
      }
      /* 下载耗时显示 */
      .download-time {
        font-size: 0.8em;
        color: #666;
        margin-top: 5px;
      }
      /* 下载统计面板 */
      .download-stats {
        margin-top: 10px;
        padding: 10px;
        background-color: #f0f0f0;
        border-radius: 4px;
        display: none;
      }
    </style>
    <!-- 添加Vue 3依赖 -->
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 页面标题 -->
      <h1>文件上传下载服务</h1>

      <!-- 文件上传区域 -->
      <div class="card">
        <h2>上传文件</h2>
        <form class="file-form" @submit.prevent="handleUpload">
          <input type="file" ref="fileInput" required />
          <button type="submit" class="btn">上传文件</button>
          <progress v-if="showUploadProgress" :value="uploadProgress" max="100"></progress>
          <div>{{ uploadStatus }}</div>
          <p class="info-text">✅ 支持上传最大1GB的文件</p>
        </form>
      </div>

      <!-- 文件列表显示区域 -->
      <div class="card">
        <h2>可用文件</h2>
        <button class="btn btn-secondary" @click="fetchFileList">刷新列表</button>
        <div id="fileListContainer">
          <ul v-if="files.length" class="file-list">
            <li v-for="file in files" :key="file.filename" class="file-item">
              <div class="file-item-header">
                <div class="file-info">
                  <span class="file-name">{{ file.filename }}</span>
                  <span class="file-size">{{ formatFileSize(file.size) }}</span>
                </div>
                <div class="file-actions">
                  <button class="btn" @click="regularDownload(file)">普通下载</button>
                  <button class="btn" @click="streamDownload(file)">在线预览</button>
                  <button class="btn" @click="fastDownload(file)">快速下载</button>
                </div>
              </div>
              <div :ref="el => setFileStatusRef(file.filename, el)" class="download-status"></div>
              <div v-if="fileDownloads[file.filename]" class="chunk-progress-container">
                <!-- 添加下载状态标题 -->
                <h4 style="margin: 0 0 10px 0">下载进度</h4>
                <div
                  v-for="(chunk, index) in fileDownloads[file.filename].chunks"
                  :key="index"
                  class="chunk-progress"
                >
                  <progress :value="chunk.progress" max="100"></progress>
                  <span class="chunk-label">{{ chunk.label }}</span>
                </div>
                <!-- 修改下载统计面板样式为始终显示 -->
                <div class="download-stats" style="display: block; margin-top: 15px">
                  <div class="download-speed">速度: {{ fileDownloads[file.filename].speed }}</div>
                  <div class="download-time">用时: {{ fileDownloads[file.filename].time }}</div>
                </div>
                <div style="margin-top: 10px">
                  <button class="btn pause-resume-btn" @click="togglePause(file.filename)">
                    {{ fileDownloads[file.filename].paused ? '继续' : '暂停' }}
                  </button>
                  <button
                    class="btn btn-danger"
                    @click="abortDownload(file.filename)"
                    style="background-color: #dc3545; margin-left: 10px"
                  >
                    中止下载
                  </button>
                </div>
              </div>
            </li>
          </ul>
          <ul v-else class="file-list">
            <li>{{ fileListStatus }}</li>
          </ul>
        </div>
      </div>
    </div>

    <!-- Vue应用代码 -->
    <script>
      const { createApp, ref, reactive, onMounted } = Vue;

      // 全局常量定义
      const CHUNK_SIZE = 1024 * 1024 * 2; // 2MB

      // 下载状态管理
      const globalDownloadState = reactive({
        downloads: {},
      });

      // 工具函数
      function formatFileSize(bytes) {
        if (bytes === 0) return '0 Bytes';
        const k = 1024;
        const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
      }

      function formatTime(milliseconds) {
        if (milliseconds < 1000) return `${milliseconds}毫秒`;
        const seconds = Math.floor(milliseconds / 1000);
        const minutes = Math.floor(seconds / 60);
        const remainingSeconds = seconds % 60;
        return minutes > 0 ? `${minutes}分${remainingSeconds}秒` : `${seconds}秒`;
      }

      // 下载管理器
      class DownloadManager {
        constructor(file, statusRef, updateState) {
          // 文件基本信息
          this.filename = file.filename;
          this.fileSize = file.size;

          // DOM引用
          this.statusRef = statusRef;

          // 状态更新回调
          this.updateState = (state) => {
            // 批量更新状态，减少Vue的响应式处理次数
            requestAnimationFrame(() => updateState(this.filename, state));
          };

          // 下载状态 - 使用普通对象而不是响应式对象
          this.startTime = Date.now();
          this.downloadedSize = 0;
          this.paused = false;
          this.aborted = false;

          // 重试和错误处理
          this.retries = new Map();
          this.maxRetries = 3;

          // 分片管理 - 使用普通数组而不是响应式数组
          this.chunks = [];
          this.downloaded = new Set();

          // 资源管理
          this.chunkStreams = new Map();
          this.activeDownloads = new Set();

          // 性能优化
          this._lastUIUpdate = Date.now();
          this._updateInterval = 200; // 限制UI更新频率，每200ms更新一次
        }

        // 更新进度
        updateProgress(index, percent, blob = null) {
          const newChunks = [...this.chunks];
          newChunks[index] = {
            ...newChunks[index],
            progress: percent,
            label: `分片 ${index + 1}: ${percent.toFixed(1)}%`,
          };

          if (blob) {
            newChunks[index].blob = blob;
          }

          // 批量更新状态
          this.updateState(this.filename, {
            chunks: newChunks,
            speed:
              formatFileSize(this.downloadedSize / ((Date.now() - this.startTime) / 1000)) + '/s',
            time: formatTime(Date.now() - this.startTime),
            paused: this.paused,
          });
        }

        // 清理资源
        cleanup() {
          this.activeDownloads.forEach((controller) => {
            try {
              controller.abort();
            } catch (e) {
              console.warn('Error aborting download:', e);
            }
          });
          this.activeDownloads.clear();

          // 清理blob对象
          this.chunks.forEach((chunk) => {
            if (chunk.blob) {
              URL.revokeObjectURL(chunk.blob);
            }
          });

          // 取消所有流
          this.chunkStreams.forEach((stream) => {
            try {
              stream.cancel();
            } catch (e) {
              console.warn('Error canceling stream:', e);
            }
          });
          this.chunkStreams.clear();
        }

        // 下载所有分片
        async downloadChunks() {
          const concurrentChunks = 3;
          let nextChunkIndex = 0;
          let failedAttempts = 0;
          const MAX_FAILED_ATTEMPTS = 3;

          // 使用requestAnimationFrame进行性能优化的UI更新
          let lastSpeedUpdate = Date.now();
          const updateSpeed = () => {
            if (this.paused || this.aborted) return;

            const now = Date.now();
            if (now - lastSpeedUpdate >= 1000) {
              // 每秒更新一次速度
              const speed =
                formatFileSize(this.downloadedSize / ((now - this.startTime) / 1000)) + '/s';
              const time = formatTime(now - this.startTime);

              requestAnimationFrame(() => {
                this.updateState(this.filename, { speed, time });
              });

              lastSpeedUpdate = now;
            }

            if (!this.paused && !this.aborted) {
              requestAnimationFrame(() => updateSpeed());
            }
          };
          updateSpeed();

          try {
            while (nextChunkIndex < this.chunks.length && !this.aborted) {
              if (this.paused) {
                await new Promise((resolve) => setTimeout(resolve, 100));
                continue;
              }

              // 使用WeakMap来存储Promise，以便垃圾回收
              const pendingDownloads = new WeakMap();
              const activeControllers = new Set();

              // 同步检查下载状态并重试缺失的分片
              const missingChunks = [];
              for (let i = 0; i < nextChunkIndex; i++) {
                if (!this.downloaded.has(i)) {
                  missingChunks.push(i);
                }
              }

              if (missingChunks.length > 0) {
                console.log(`重新下载缺失的分片: ${missingChunks.join(', ')}`);
                // 并发下载缺失的分片
                const retryPromises = missingChunks.slice(0, concurrentChunks).map((index) => {
                  const controller = new AbortController();
                  activeControllers.add(controller);
                  const promise = this.downloadChunk(index, this.chunks[index]);
                  pendingDownloads.set(controller, promise);
                  return promise;
                });

                failedAttempts++;
                if (failedAttempts >= MAX_FAILED_ATTEMPTS) {
                  throw new Error('下载失败次数过多，请重试');
                }

                await Promise.allSettled(retryPromises);
              }

              // 下载新的分片
              const newChunksToDownload = [];
              while (
                newChunksToDownload.length < concurrentChunks &&
                nextChunkIndex < this.chunks.length
              ) {
                if (!this.downloaded.has(nextChunkIndex)) {
                  newChunksToDownload.push(nextChunkIndex);
                }
                nextChunkIndex++;
              }

              if (newChunksToDownload.length > 0) {
                const downloadPromises = newChunksToDownload.map((index) => {
                  const controller = new AbortController();
                  activeControllers.add(controller);
                  const promise = this.downloadChunk(index, this.chunks[index]);
                  pendingDownloads.set(controller, promise);
                  return promise;
                });

                try {
                  await Promise.allSettled(downloadPromises);
                  failedAttempts = 0; // 重置失败计数
                } catch (error) {
                  console.error('分片下载失败:', error);
                  await new Promise((resolve) => setTimeout(resolve, 1000));
                }
              }

              // 清理已完成的下载
              activeControllers.forEach((controller) => {
                if (controller.signal.aborted) {
                  activeControllers.delete(controller);
                  pendingDownloads.delete(controller);
                }
              });
            }

            // 最终验证
            const finalCheck = Array.from(Array(this.chunks.length).keys())
              .filter((i) => !this.downloaded.has(i))
              .map((i) => i + 1);

            if (finalCheck.length > 0) {
              throw new Error(`仍有分片未完成下载: ${finalCheck.join(', ')}`);
            }
          } catch (error) {
            console.error('Download failed:', error);
            this.statusRef.textContent = error.message || '下载失败，请重试';
            throw error;
          }
        }

        // 下载单个分片
        async downloadChunk(index, { start, end }) {
          if (this.aborted) return;

          const retryCount = this.retries.get(index) || 0;
          if (retryCount >= this.maxRetries) {
            throw new Error(`分片 ${index + 1} 下载失败次数过多`);
          }

          const controller = new AbortController();
          this.activeDownloads.add(controller);

          try {
            const response = await fetch(`/files/download/${this.filename}`, {
              headers: {
                Range: `bytes=${start}-${end}`,
                'Cache-Control': 'no-cache',
              },
              signal: controller.signal,
            });

            if (!response.ok) {
              throw new Error(`分片 ${index + 1} 下载失败: ${response.status}`);
            }

            const reader = response.body.getReader();
            const contentLength = parseInt(response.headers.get('Content-Length'), 10);
            let receivedLength = 0;
            const chunks = [];
            let lastProgressUpdate = Date.now();

            // 使用 while 循环读取数据流
            while (!this.aborted) {
              const { done, value } = await reader.read();
              if (done) break;

              chunks.push(value);
              receivedLength += value.length;
              this.downloadedSize += value.length;

              // 限制进度更新频率，每200ms更新一次
              const now = Date.now();
              if (now - lastProgressUpdate >= 200) {
                const progress = (receivedLength / contentLength) * 100;
                // 使用数组替换而不是直接修改对象属性，减少响应式更新
                this.chunks = this.chunks.map((chunk, i) =>
                  i === index
                    ? { ...chunk, progress, label: `分片 ${index + 1}: ${progress.toFixed(1)}%` }
                    : chunk,
                );
                lastProgressUpdate = now;
              }
            }

            // 只有在没有中止的情况下才完成下载
            if (!this.aborted) {
              const blob = new Blob(chunks, { type: 'application/octet-stream' });

              // 验证分片大小
              if (blob.size !== contentLength) {
                throw new Error(
                  `分片 ${index + 1} 数据不完整 (期望 ${contentLength} 字节, 实际 ${blob.size} 字节)`,
                );
              }

              // 一次性更新状态
              this.chunks = this.chunks.map((chunk, i) =>
                i === index
                  ? { ...chunk, blob, progress: 100, label: `分片 ${index + 1}: 完成` }
                  : chunk,
              );
              this.downloaded.add(index);
            }

            controller.abort(); // 标记下载完成
            this.activeDownloads.delete(controller);
          } catch (error) {
            if (!this.aborted) {
              console.error(`分片 ${index + 1} 下载失败:`, error);
              this.retries.set(index, retryCount + 1);
              if (retryCount < this.maxRetries) {
                await new Promise((resolve) => setTimeout(resolve, 1000 * (retryCount + 1)));
                this.activeDownloads.delete(controller);
                return this.downloadChunk(index, { start, end });
              }
              throw error;
            }
          } finally {
            if (this.activeDownloads.has(controller)) {
              this.activeDownloads.delete(controller);
            }
          }
        }

        // 合并并下载完整文件
        async mergeAndDownload() {
          try {
            // 验证所有分片状态
            const missingChunks = [];
            const invalidChunks = [];
            let totalSize = 0;

            // 全面检查所有分片
            for (let i = 0; i < this.chunks.length; i++) {
              const chunk = this.chunks[i];
              if (!chunk.blob || !(chunk.blob instanceof Blob)) {
                missingChunks.push(i + 1);
                continue;
              }

              // 验证分片大小
              const expectedSize = chunk.end - chunk.start + 1;
              if (chunk.blob.size !== expectedSize) {
                invalidChunks.push(
                  `分片${i + 1}(期望${expectedSize}字节, 实际${chunk.blob.size}字节)`,
                );
              }

              totalSize += chunk.blob.size;
            }

            // 报告所有错误
            const errors = [];
            if (missingChunks.length > 0) {
              errors.push(`缺少分片: ${missingChunks.join(', ')}`);
            }
            if (invalidChunks.length > 0) {
              errors.push(`大小不匹配: ${invalidChunks.join(', ')}`);
            }
            if (totalSize !== this.fileSize) {
              errors.push(`总大小不匹配(期望${this.fileSize}字节, 实际${totalSize}字节)`);
            }

            if (errors.length > 0) {
              throw new Error('文件验证失败:\n' + errors.join('\n'));
            }

            // 开始合并
            this.statusRef.textContent = '正在合并文件...';
            // 使用 Array.from 创建新数组，避免 Vue 的响应式代理影响性能
            const blobs = Array.from(this.chunks, (chunk) => chunk.blob);

            // 创建合并后的Blob
            const finalBlob = new Blob(blobs, { type: 'application/octet-stream' });

            // 清理分片blob
            blobs.length = 0;
            this.chunks.forEach((chunk) => {
              if (chunk.blob) {
                URL.revokeObjectURL(chunk.blob);
                chunk.blob = null;
              }
            });

            // 创建下载链接
            const url = URL.createObjectURL(finalBlob);
            const a = document.createElement('a');
            a.href = url;
            a.download = this.filename;
            document.body.appendChild(a);

            // 触发下载
            a.click();
            document.body.removeChild(a);

            this.statusRef.textContent = `下载完成 | 总耗时: ${formatTime(Date.now() - this.startTime)}`;

            // 延迟清理资源
            setTimeout(() => {
              URL.revokeObjectURL(url);
              this.cleanup();
              delete globalDownloadState.downloads[this.filename];
            }, 1000);
          } catch (error) {
            console.error('合并文件失败:', error);
            this.statusRef.textContent = error.message || '合并文件失败，请重试';

            // 重置下载状态，便于重试
            this.cleanup();
            delete globalDownloadState.downloads[this.filename];
            throw error;
          }
        }

        // 开始下载
        async start() {
          const chunkSize = Math.min(CHUNK_SIZE, Math.ceil(this.fileSize / 10));
          // 初始化分片数组
          for (let start = 0; start < this.fileSize; start += chunkSize) {
            const end = Math.min(start + chunkSize - 1, this.fileSize - 1);
            this.chunks.push({
              start,
              end,
              progress: 0,
              label: `分片 ${this.chunks.length + 1}: 0%`,
              blob: null,
            });
          }

          // 初始化下载状态
          this.updateState(this.filename, {
            chunks: [...this.chunks],
            speed: '0 B/s',
            time: '0秒',
            paused: false,
          });

          await this.downloadChunks();
          if (!this.aborted && this.downloaded.size === this.chunks.length) {
            await this.mergeAndDownload();
          }
        }
      }

      // Vue应用主组件
      const App = {
        setup() {
          // 状态定义
          const files = ref([]);
          const fileListStatus = ref('加载文件列表...');
          const uploadProgress = ref(0);
          const uploadStatus = ref('');
          const showUploadProgress = ref(false);
          const fileInput = ref(null);
          const fileStatusRefs = reactive({});
          const fileDownloads = reactive(globalDownloadState.downloads);

          // 文件状态引用设置
          const setFileStatusRef = (filename, el) => {
            if (el) fileStatusRefs[filename] = el;
          };

          // 获取文件列表
          const fetchFileList = async () => {
            try {
              const response = await fetch('/files/list');
              if (!response.ok) throw new Error('获取文件列表失败');
              files.value = await response.json();
              fileListStatus.value = files.value.length ? '' : '没有可用文件';
            } catch (error) {
              fileListStatus.value = `错误: ${error.message}`;
            }
          };

          // 处理文件上传
          const handleUpload = async (e) => {
            e.preventDefault();
            const file = fileInput.value.files[0];
            if (!file) {
              uploadStatus.value = '请选择文件';
              return;
            }

            if (file.size > 1024 * 1024 * 1024) {
              uploadStatus.value = '文件过大，最大支持1GB';
              return;
            }

            const formData = new FormData();
            formData.append('file', file);
            showUploadProgress.value = true;
            uploadProgress.value = 0;
            uploadStatus.value = '开始上传...';

            try {
              const xhr = new XMLHttpRequest();
              xhr.timeout = 60000;

              xhr.upload.onprogress = (e) => {
                if (e.lengthComputable) {
                  uploadProgress.value = (e.loaded / e.total) * 100;
                  uploadStatus.value = `上传中: ${Math.round(uploadProgress.value)}%`;
                }
              };

              xhr.onload = () => {
                if (xhr.status === 200 || xhr.status === 201) {
                  uploadStatus.value = '上传成功！';
                  fileInput.value.value = '';
                  fetchFileList();
                } else {
                  uploadStatus.value = `上传失败: ${xhr.statusText || '服务器错误'}`;
                }
                showUploadProgress.value = false;
              };

              xhr.onerror = () => {
                uploadStatus.value = '上传失败: 网络错误';
                showUploadProgress.value = false;
              };

              xhr.ontimeout = () => {
                uploadStatus.value = '上传超时，请重试';
                showUploadProgress.value = false;
              };

              xhr.open('POST', '/files/upload', true);
              xhr.send(formData);
            } catch (error) {
              uploadStatus.value = `上传失败: ${error.message}`;
              showUploadProgress.value = false;
            }
          };

          // 普通下载
          const regularDownload = async (file) => {
            const statusRef = fileStatusRefs[file.filename];
            let startTime = Date.now();
            statusRef.textContent = '开始下载...';

            try {
              const response = await fetch(file.url, { method: 'HEAD' });
              const contentLength = response.headers.get('content-length');

              // 创建下载链接
              const a = document.createElement('a');
              a.href = file.url;
              a.style.display = 'none';
              document.body.appendChild(a);
              a.click();
              document.body.removeChild(a);

              // 估算下载时间
              const estimatedTime = Math.ceil(contentLength / (1024 * 1024));
              setTimeout(() => {
                const duration = Date.now() - startTime;
                statusRef.textContent = `下载完成 | 总耗时: ${formatTime(duration)}`;
              }, estimatedTime * 1000);
            } catch (error) {
              console.error('下载失败:', error);
              statusRef.textContent = '下载失败，请重试';
            }
          };

          // 流式下载（在线预览）
          const streamDownload = (file) => {
            const statusRef = fileStatusRefs[file.filename];
            const startTime = Date.now();
            statusRef.textContent = '开始加载...';

            window.open(`/files/stream/${file.filename}`, '_blank');

            // 更新加载状态
            setTimeout(() => {
              const duration = Date.now() - startTime;
              statusRef.textContent = `加载完成 | 总耗时: ${formatTime(duration)}`;
            }, 1000);
          };

          // 快速下载（分片）
          const fastDownload = (file) => {
            if (fileDownloads[file.filename]) {
              alert('该文件已在下载中');
              return;
            }

            // 初始化下载状态
            fileDownloads[file.filename] = {
              chunks: [],
              paused: false,
              speed: '0 B/s',
              time: '0秒',
              startTime: Date.now(),
            };

            const updateState = (filename, state) => {
              if (!fileDownloads[filename]) return; // 防止未初始化
              Object.assign(fileDownloads[filename], state);
            };

            const manager = new DownloadManager(file, fileStatusRefs[file.filename], updateState);
            manager.start();
          };

          // 暂停/继续下载
          const togglePause = (filename) => {
            const download = fileDownloads[filename];
            if (download) {
              download.paused = !download.paused;
            }
          };

          // 中止下载
          const abortDownload = (filename) => {
            const download = fileDownloads[filename];
            if (download) {
              download.aborted = true;
              delete fileDownloads[filename];
            }
          };

          // 生命周期钩子
          onMounted(() => {
            fetchFileList();
          });

          return {
            files,
            fileListStatus,
            uploadProgress,
            uploadStatus,
            showUploadProgress,
            fileInput,
            fileDownloads,
            setFileStatusRef,
            fetchFileList,
            handleUpload,
            regularDownload,
            streamDownload,
            fastDownload,
            togglePause,
            abortDownload,
            formatFileSize,
          };
        },
      };

      // 创建Vue应用
      createApp(App).mount('#app');
    </script>
  </body>
</html>
